﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using W4S.W4SWorkItemTrackingPackage;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Data;
using System.Collections;
using Microsoft.VisualStudio.TeamFoundation.WorkItemTracking;

namespace W4S.Work4Source
{
    /// <summary>
    /// Interaction logic for MyControl.xaml
    /// </summary>
    public partial class MyControl : UserControl
    {
        public MyControl()
        {
            InitializeComponent();
            btnCancel.IsEnabled = false;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            StopSearch();
            btnCancel.IsEnabled = false;
            btnWorkItems.IsEnabled = true;
        }

        private void btnWorkItems_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                GetSelectedExplorerItem();
                if (ExplorerItem == null) return;
                lblSelectedItem.Content = ExplorerItem.ServerItem;
                btnCancel.IsEnabled = true;
                btnWorkItems.IsEnabled = false;
                StartSearch();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            finally
            {
                Cursor = Cursors.Arrow;
            }
        }

        private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            ShowWorkItem();
        }

        private void GetSelectedExplorerItem()
        {
            VersionControlExplorerItem[] explorerItem = VSEnvironment.VersionControlExplorerExt.SelectedItems;
            if (explorerItem.Length == 1)
            {
                ExplorerItem = VSEnvironment.VersionControlServer.GetItem(explorerItem[0].SourceServerPath);
            } 
            else
            {
                ExplorerItem = null;
                MessageBox.Show("Please select one file or folder to retrieve linked work item for",
                                   "Select file or folder", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private Item ExplorerItem
        {
            get { return _explorerItem; }
            set { _explorerItem = value; }
        }

        #region "Async Calls to Get Work Items"

        private void StartSearch()
        {
            _workerThread = new Thread(DisplayWorkItems);
            _workerThread.Start();
        }

        private void StopSearch()
        {
            if (_workerThread.IsAlive) _workerThread.Abort();
        }
        
        private void DisplayWorkItems()
        {
            CreateTable();
            LoadWorkItems();
            LoadWorkItemsFromChangesets();
            LoadGrid();
        }
    
      #endregion

        private void CreateTable()
        {
            // Create sample WorkItems table.
            _workItemsTable = new DataTable();
            _workItemsTable.TableName = "WorkItems";
            _workItemsTable.Columns.Add("Work Item", typeof(string));
            _workItemsTable.Columns.Add("Changeset", typeof(string));
            _workItemsTable.Columns.Add("Type", typeof(string));
            _workItemsTable.Columns.Add("Title", typeof(string));
            _workItemsTable.Columns.Add("State", typeof(string));
        }

        private void LoadWorkItems()
        {
            foreach (WorkItem item in GetWorkItems(new List<string>() { ExplorerItem.ArtifactUriLatestItemVersion.ToString() }.ToArray()))
            {
                InsertDataIntoTable(item.Id.ToString(), "", item.Type.Name, item.Title, item.State);
            }
            _workItemsTable.AcceptChanges();
        }

        private void LoadWorkItemsFromChangesets()
        {
            foreach (KeyValuePair<string, WorkItem> item in GetChangeSetWorkItems())
            {
                string[] changesetId = item.Key.Split(new Char[] { ':' });
                InsertDataIntoTable(item.Value.Id.ToString(), changesetId[1], item.Value.Type.Name, item.Value.Title, item.Value.State);
            }
            _workItemsTable.AcceptChanges();
        }

        private void LoadGrid()
        {
            Dispatcher.Invoke(
                DispatcherPriority.Normal,
                new Action(
                    delegate()
                        {
                            DataSet dataSet = new DataSet("WorkItems");
                            dataSet.Tables.Add(_workItemsTable);
                            dataGrid.ItemsSource = dataSet.Tables[0].DefaultView;
                            FormatGrid();
                            btnCancel.IsEnabled = false;
                            btnWorkItems.IsEnabled = true;
                        }
                    ));
        }

        private void FormatGrid()
        {
            foreach (DataGridTextColumn column in dataGrid.Columns)
            {
                switch (column.Header.ToString())
                {
                    case "Work Item": column.Width = 100;
                        break;
                    case "Changeset": column.Width = 100;
                        break;
                    case "Type": column.Width = 100;
                        break;
                    case "Title": column.Width = 600;
                        break;
                    case "State": column.Width = 100;
                        break;
                }
            }
        }

        private void InsertDataIntoTable(string workItemID,string changesetID,string type,string title,string state)
        {
            List<string> rowDetails = new List<string>();
            rowDetails.Add(workItemID);
            rowDetails.Add(changesetID);
            rowDetails.Add(type);
            rowDetails.Add(title);
            rowDetails.Add(state);
            _workItemsTable.Rows.Add(rowDetails.ToArray()) ;
        }

        /// <summary>
        /// Return list of work items related to a source file
        /// </summary>
        private List<WorkItem> GetWorkItems(string[] sourceFileUrl)
        {
            List<WorkItem> workItems = new List<WorkItem>();
            Dictionary<string, int[]> workItemDictionary = VSEnvironment.WorkItemStore.GetWorkItemIdsForArtifactUris(sourceFileUrl, DateTime.Now);
            foreach (KeyValuePair<string, int[]> workItem in workItemDictionary)
            {
                if (workItem.Value != null)
                {
                    foreach (int workItemId in workItem.Value)
                    {
                        if (!workItems.Contains(VSEnvironment.WorkItemStore.GetWorkItem(workItemId)))
                            workItems.Add(VSEnvironment.WorkItemStore.GetWorkItem(workItemId));
                    }
                }
            }
            return workItems;
        }

        /// <summary>
        /// Get the work items linked to a changeset.
        /// </summary>
        private Dictionary<string, WorkItem> GetChangeSetWorkItems()
        {
            Dictionary<string, WorkItem> changesetWorkItem = new Dictionary<string, WorkItem>();
            if (ExplorerItem != null)
            {
                _changeSetsList = new List<Changeset>();
                _changesetIDs = new List<int>();                                      
                GetChangeSets();
                GetChangeSetsForBrachedItems();
                
                List<int> workItemIDs = new List<int>();
                int changesetCount = 0;
                foreach (Changeset changeset in _changeSetsList)
                {
                    foreach (WorkItem wit in changeset.WorkItems)
                    {
                        if (!workItemIDs.Contains(wit.Id))
                        {
                            workItemIDs.Add(wit.Id);
                            changesetWorkItem.Add(changesetCount.ToString() + ":" + changeset.ChangesetId.ToString(), wit);
                            changesetCount += 1;
                        }
                    }
                }
            }
            return changesetWorkItem;
        }

        /// <summary>
        /// Get the work items linked to a changeset by using the same mechanism to return the "History" of a file
        /// </summary>
        private void GetChangeSets()
        {
            QueryHistory(ExplorerItem.ServerItem);
        }

        /// <summary>
        /// Return changesets of brached items. 
        /// </summary>
        private void GetChangeSetsForBrachedItems()
        {
          BranchHistoryTreeItem[][] branchHistory = VSEnvironment.VersionControlServer.GetBranchHistory(
                    new ItemSpec[] { new ItemSpec(ExplorerItem.ServerItem, RecursionType.Full) }, VersionSpec.Latest);
   
            foreach (BranchHistoryTreeItem[] treeItemArray in branchHistory)
            {
                foreach (BranchHistoryTreeItem treeItem in treeItemArray)
                {
                    GetChildrenOfBrachedItem(treeItem);
                }
            }
        }

        /// <summary>
        /// Iterate children of children to find all changesets
        /// </summary>
        /// <remarks>Doing recursive search to coupe with if an item might have been brached\merged multiple times</remarks>
        private void GetChildrenOfBrachedItem(BranchHistoryTreeItem historyTreeItem)
        {
            foreach (BranchHistoryTreeItem child in historyTreeItem.Children)
            {
                //BrachFromItem holds the path of where the file was stored before it was moved into is new location.
                Item item = child.Relative.BranchFromItem;
                if (!_changesetIDs.Contains(item.ChangesetId))
                {
                    QueryHistory(item.ServerItem);
                }
                GetChildrenOfBrachedItem(child);
            }
        }

        private void QueryHistory(string serverItem)
        {
            // Null means All
            VersionSpec includeAll = null;
            IEnumerable changeSets = VSEnvironment.VersionControlServer.QueryHistory(
                serverItem, VersionSpec.Latest, 0, RecursionType.Full,
                null, includeAll, includeAll, Int32.MaxValue, true, false);
            try
            {
                foreach (Changeset changeset in changeSets)
                {
                    if (!_changesetIDs.Contains(changeset.ChangesetId))
                    {
                        _changeSetsList.Add(changeset);
                        _changesetIDs.Add(changeset.ChangesetId);
                    }
                }
            } 
            catch (InvalidOperationException)
            {
                  //handle exception when _workerThread has been aborted  
            }
        }

        private void ShowWorkItem()
        {
            DataRowView rowView = (DataRowView)dataGrid.SelectedItem;
            int workItemId = Convert.ToInt32(rowView.Row.ItemArray[0]);
            IWorkItemDocument workItemDocument = VSEnvironment.DocumentService.GetWorkItem(VSEnvironment.ProjectCollection, workItemId, dataGrid); ;
            try
            {
                VSEnvironment.DocumentService.ShowWorkItem(workItemDocument);
            }
            finally
            {
                workItemDocument.Release(dataGrid);
            }
        }

        private DataTable _workItemsTable;
        private List<Changeset> _changeSetsList;
        private List<int> _changesetIDs;
        private Thread _workerThread;
        private Item _explorerItem;

    }
}